using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace ArkanoidX
{
    /// <summary>
    /// This is a _game component that implements IUpdateable.
    /// </summary>
    public partial class Racket : StatefulComponent, IPhysicalObject
    {
        public abstract class State : StatefulComponent.State<Racket>
        {
            protected Rectangle _reachableArea;

            public Rectangle ReachableArea
            {
                get { return _reachableArea; }
            }

            public State(Texture2D texture, Vector2 size, Rectangle regionInSource,
                Rectangle reachableArea)
                : base(texture, size, regionInSource)
            {
                _reachableArea = reachableArea;
            }

            public State(Texture2D texture, Vector2 size, Rectangle reachableArea)
                : base(texture, size)
            {
                _reachableArea = reachableArea;
            }

            public State(Texture2D texture, Rectangle reachableArea)
                : base(texture)
            {
                _reachableArea = reachableArea;
            }
        }

        protected TimeSpan _lastRealTime = default(TimeSpan);

        bool _disposed = false;         // flag that indicates whether the object has been disposed

        protected Vector2 _position;    // Position on screen
        protected Rectangle _rectangle; // Rectangle which represents where the sprite picture is

        protected Vector2 _lastPosition;
        protected Vector2 _velocity;


        public State[] _states;
        public int _iCurrentState;        // index to the current state of the racket

        public State CurrentState { get { return _states[_iCurrentState]; } }

        // required by IPhysicalObject
        public float Weight
        {
            get { throw new NotImplementedException(); }
        }


        public Rectangle ReachableArea
        {
            get { return CurrentState.ReachableArea; }
        }

        public Vector2 Position
        {
            get { return _position; }
        }

        public Racket(Game game, State[] states)
            : base(game)
        {
            _states = states;
            _iCurrentState = 0;

            _position = new Vector2();
            
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the _game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the _game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            CurrentState.Update(this, gameTime);

            if (_lastRealTime != default(TimeSpan))
            {
                TimeSpan timeDiff = gameTime.TotalRealTime - _lastRealTime;
                Vector2 posDiff = _position - _lastPosition;
                _velocity = posDiff / timeDiff.Milliseconds;
            }

            _lastRealTime = gameTime.TotalRealTime;
            _lastPosition = _position;

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            // Get the current sprite batch
            SpriteBatch sBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));

            CurrentState.Draw(sBatch, _position, Color.White);

            base.Draw(gameTime);
        }

        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // Cleanup managed objects
                    foreach (State state in this._states)
                    {
                        state.Texture.Dispose();
                    }
                }
                // Cleanup unmanaged objects if any
                // ...
            }

            _disposed = true;
        }

        public void Collides(IPhysicalObject phyObj)
        {
            /*
             * Warning: Keep the algorithm simple!
             */

            if (phyObj is Ball)
            {
                Ball ball = (Ball)phyObj;

                int top = (int)Math.Ceiling(this.Position.Y - CurrentState.HalfSize.Y);
                int bottom = (int)Math.Floor(this.Position.Y + CurrentState.HalfSize.Y);
                int left = (int)Math.Ceiling(this.Position.X - CurrentState.HalfSize.X);
                int right = (int)Math.Floor(this.Position.X + CurrentState.HalfSize.X);

                Vector2 hitPoint;
                Utility.HitBorder hitBorder;

                bool collided = Utility.Collide(ball.Position, ball.Radius, 
                    new Rectangle(left, top, right - left, bottom - top),
                    out hitPoint, out hitBorder);

                switch (hitBorder)
                {
                    case Utility.HitBorder.Top:
                        if (ball.Velocity.Y > _velocity.Y)
                        {
                            Random r = new Random();
                            float vx = ball.Velocity.X;
                            float vy = ball.Velocity.Y;
                            float v = (float)Math.Sqrt(vx * vx + vy * vy);
                            float dx = hitPoint.X - this.Position.X;
                            float halfWidth = CurrentState.HalfSize.X;
                            float xrate = dx / halfWidth;
                            xrate = (float)Math.Min(Math.Max(xrate, -0.99), 0.99);
                            float angle = xrate * (float)Math.PI * 0.5f;

                            vx = v * (float)Math.Sin(angle);
                            vy = -v * (float)Math.Cos(angle);

                            // ball driven by the racket
                            //vx += this._velocity.X;
                            //vy += this._velocity.Y;

                            ball.Velocity = new Vector2(vx, vy);
                        }
                        break;
                }
            }
        }

        public void PutInStartPosition()
        {
            _position.X = ReachableArea.Left + ReachableArea.Width / 2;
            _position.Y = ReachableArea.Bottom - this.CurrentState.Size.Y / 2;

            _iCurrentState = 1;
        }
    }
}